﻿Imports System.Net.Sockets
Imports System.Threading
Imports NLog

Public Class cSroTcpL
    Inherits cConnectionBase

    Protected pSocket As Socket
    Protected pLSocket As Socket
    Protected pBuffer(&H7FFF - 1) As Byte

    Protected pFisch As New BlowfishNET.BlowfishECB(New Byte() {&HD, &H3, &H3D, &H3, &H3, &HD, &H3, &H1D}, 0, 8)

    WithEvents socketEvent1 As SocketAsyncEventArgs

    Protected Logger As NLog.Logger = LogManager.GetLogger("ToClientLogger")

    Dim A5000_1 As Byte() = {&H25, 0, 0, &H50, 0, 0, &HE, &H17, &H7D, &H4E, &H92, &H97, &HF8, &H8, &HFE, &H8A, &H0, &H0, &H0, &HF2, &H0, &H0, &H0, &H85, &HD8, &H1D, &H98, &H28, &HC7, &H1C, &HCF, &H1, &H0, &H0, &H0, &H1, &H0, &H0, &H0, &H4A, &HCC, &HEA, &H22}
    Dim A5000_2 As Byte() = {&H9, 0, 0, &H50, 0, 0, &H10, &H44, &H4E, &HA8, &H2F, &H86, &HC5, &H6D, &H7A}


    Sub New()
        pLSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP)
        pLSocket.ExclusiveAddressUse = False
    End Sub

    Public Overrides Sub Start(ByVal ip As String, ByVal port As UInt32)
        Set_Status(eSocketStatus.Accepting)
        Try
            If pLSocket.IsBound = False Then
                Dim ep As New System.Net.IPEndPoint(Net.IPAddress.Parse(ip), port)
                pLSocket.Bind(ep)
                pLSocket.Listen(2)
            End If
            Console.SetCursorPosition(0, 14)
            Console.WriteLine("Client --> Accepting --> {0}:{1}", ip, port)
            If socketEvent1 IsNot Nothing Then socketEvent1.Dispose()
            socketEvent1 = New SocketAsyncEventArgs
            pLSocket.AcceptAsync(socketEvent1)
        Catch ex As Exception
            Set_Status(eSocketStatus.Offline)
            Console.WriteLine(ex.Message.ToString)
        End Try
    End Sub

    Public Overrides Sub Disconnect()
        If Status <> eSocketStatus.Offline Then
            Set_Status(eSocketStatus.Offline)
            Console.SetCursorPosition(0, 14)
            Console.WriteLine("Client --> Disconnect")
        End If
        Try
            pSocket.Shutdown(SocketShutdown.Both)
        Catch ex As Exception
        Finally
            If pSocket IsNot Nothing Then
                pSocket.Close()
            End If
        End Try
    End Sub

    Public Overrides Sub Send(ByRef packet() As Byte)
        Logger.Info(BitConverter.ToString(packet))

        Dim flags As UInt32 = BitConverter.ToUInt16(packet, 4)

        packet(4) = 0
        packet(5) = 0

        If (flags And &H100) <> 0 Then 'crypted
            packet(1) = packet(1) Or &H80 'set hi bit to 1 
            Dim lenoffset As Byte = (packet.GetUpperBound(0) + 1 - 2) Mod 8
            If lenoffset <> 0 Then
                lenoffset = 8 - lenoffset
                ReDim Preserve packet(packet.GetUpperBound(0) + lenoffset)
            End If
            pFisch.EncryptRev(packet, 2, packet, 2, packet.GetUpperBound(0) + 1 - 2)
        End If

        Try
            pSocket.Send(packet)
        Catch ex As Exception
            Console.SetCursorPosition(0, 14)
            Console.WriteLine("Client send error " & ex.Message.ToString)
        End Try
    End Sub

    Private Sub socketEvent1_Completed(ByVal sender As Object, ByVal e As System.Net.Sockets.SocketAsyncEventArgs) Handles socketEvent1.Completed
        If e.SocketError = SocketError.Success Then

            Select Case e.LastOperation
                Case SocketAsyncOperation.Accept
                    Console.SetCursorPosition(0, 14)
                    Console.WriteLine("Client --> Accepted")
                    pSocket = socketEvent1.AcceptSocket
                    socketEvent1.SetBuffer(pBuffer, 0, pBuffer.GetUpperBound(0) + 1)
                    pSocket.ReceiveAsync(socketEvent1)
                    Set_Status(eSocketStatus.Handshake)
                    Send(A5000_1) 'say hallo to client
                Case SocketAsyncOperation.Receive
                    ProcessReceive(sender, e)
                Case Else
                    Console.WriteLine(e.LastOperation.ToString)
            End Select

        Else
            Console.WriteLine(e.LastOperation.ToString & "  " & e.SocketError.ToString)
            Disconnect()
        End If

    End Sub

    Private Sub ProcessReceive(ByVal sender As Object, ByVal e As System.Net.Sockets.SocketAsyncEventArgs)
        If e.BytesTransferred = 0 Then
            Disconnect()
            Exit Sub
        End If

        Dim len As Int32 = -1
        Dim byteCounter As Int32 = e.BytesTransferred + e.Offset

        If e.Offset <> 0 Then
            socketEvent1.SetBuffer(0, pBuffer.GetUpperBound(0) + 1)
        End If

        Using r As New IO.BinaryReader(New IO.MemoryStream(e.Buffer, 0, byteCounter))
            Do
                Dim PacketStartPos As UInt32 = r.BaseStream.Position

                Dim crypted As Boolean = False
                len = -1
                Dim lenoffset As Byte = 0
                If r.BaseStream.Position + 2 <= byteCounter Then 'can read Len ?
                    len = r.ReadUInt16()
                    If len >> 15 = 1 Then       'check if crypted
                        crypted = True          'mark it as crypted
                        len = len And &H7FFF    'remove HI Bit
                        'check if we need lenoffset to fit blowfish requirement(divisible by 8)
                        lenoffset = (len + 4) Mod 8
                        If lenoffset <> 0 Then lenoffset = 8 - lenoffset
                    End If
                End If

                If r.BaseStream.Position + 4 + len + lenoffset <= byteCounter Then 'check if packet complett
                    r.BaseStream.Position = PacketStartPos
                    Dim packet As Byte() = r.ReadBytes(len + lenoffset + 6)
                    If crypted = True Then
                        pFisch.DecryptRev(packet, 2, packet, 2, len + 4 + lenoffset)
                        If lenoffset <> 0 Then
                            ReDim Preserve packet(len + 6 - 1)
                        End If
                        packet(4) = 2 'to server
                        packet(5) = 1 'crypted
                        packet(0) = len And &HFF
                        packet(1) = len >> 8
                    Else
                        packet(4) = 2 'to server
                        packet(5) = 0 'not crypted
                    End If
                    If Status = eSocketStatus.Handshake Then
                        Send(A5000_2)
                        Console.SetCursorPosition(0, 14)
                        Console.WriteLine("Client --> Handshake Done")
                        Set_Status(eSocketStatus.Connected)
                    Else
                        Add_Packet(packet)
                    End If
                Else
                    len = -1
                End If

                If len = -1 Then 'no complett packet 
                    r.BaseStream.Position = PacketStartPos
                    Using w As New IO.BinaryWriter(New IO.MemoryStream(pBuffer))
                        w.Write(r.ReadBytes(r.BaseStream.Length - PacketStartPos))
                        w.Flush()
                        socketEvent1.SetBuffer(pBuffer, r.BaseStream.Length - PacketStartPos, pBuffer.GetUpperBound(0) - (r.BaseStream.Length - PacketStartPos) + 1)
                    End Using
                End If
            Loop Until r.BaseStream.Position = r.BaseStream.Length
        End Using

        Try
            pSocket.ReceiveAsync(socketEvent1)
        Catch ex As Exception
            Disconnect()
        End Try
    End Sub
End Class
